﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;

namespace Common
{
    /// <summary>
    /// 遍历目录（包含子目录）
    /// </summary>
    public class FindFiles
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr FindFirstFile(string pFileName, ref WIN32_FIND_DATA pFindFileData);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool FindNextFile(IntPtr hndFindFile, ref WIN32_FIND_DATA lpFindFileData);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FindClose(IntPtr hndFindFile);

        Stack<string> m_scopes = new Stack<string>();
        private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
        WIN32_FIND_DATA FindFileData;
        IntPtr hFind = INVALID_HANDLE_VALUE;

        public void Find(string rootDir, ref List<string> Files)
        {
            string path = rootDir;

            try
            {
                start:
                new FileIOPermission(FileIOPermissionAccess.PathDiscovery, Path.Combine(path, ".")).Demand();
                if (path[path.Length - 1] != '\\')
                {
                    path = path + "\\";
                }
                hFind = FindFirstFile(Path.Combine(path, "*"), ref FindFileData);
                if (hFind != INVALID_HANDLE_VALUE)
                {
                    do
                    {
                        if (FindFileData.cFileName.Equals(@".") || FindFileData.cFileName.Equals(@".."))
                            continue;
                        if ((FindFileData.dwFileAttributes & 0x10) != 0)
                        {
                            string file = Path.Combine(path, FindFileData.cFileName);

                            m_scopes.Push(file);
                        }
                        else
                        {
                            Files.Add(path + FindFileData.cFileName);
                        }
                    }
                    while (FindNextFile(hFind, ref FindFileData));
                }
                FindClose(hFind);
                if (m_scopes.Count > 0)
                {
                    path = m_scopes.Pop();

                    goto start;
                }
            }
            catch
            {

            }
        }
    }

    [Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), BestFitMapping(false)]
    internal struct WIN32_FIND_DATA
    {
        public int dwFileAttributes;
        public int ftCreationTime_dwLowDateTime;
        public int ftCreationTime_dwHighDateTime;
        public int ftLastAccessTime_dwLowDateTime;
        public int ftLastAccessTime_dwHighDateTime;
        public int ftLastWriteTime_dwLowDateTime;
        public int ftLastWriteTime_dwHighDateTime;
        public int nFileSizeHigh;
        public int nFileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }
}
